#include "QuadraticCurve.h"

QuadraticCurve::QuadraticCurve() : Graphic()
{
}

QuadraticCurve::~QuadraticCurve()
{
}

void QuadraticCurve::draw()
{
    // load shader if not loaded
    if (this->shader == NULL)
    {
        loadShader(SHADER_FN);
    }

    if (this->isCurveClosed)
    {
        //drawInnerPolygon(); // drawing the inner portion
        drawEdgeTriangles(); // drawing the curve edges
    }
    else
    {
        drawOpenCurve();
    }
}

void QuadraticCurve::drawInnerPolygon()
{
    PolyMesh& inner = this->innerMesh;

    PolyMesh::ConstFaceIter fIter;
    PolyMesh::ConstFaceVertexIter fvIter;

    glColor3f(this->color[0], 
              this->color[1], 
              this->color[2]);
    glBegin(GL_TRIANGLES);
        fIter = inner.faces_begin();
        do
        {
            fvIter = inner.cfv_iter(fIter);
            do
            {
                glVertex3fv(&inner.point(fvIter)[0]);
                ++fvIter;
            }
            while(fvIter != false);
            ++fIter;
        }
        while(fIter != inner.faces_end());
    glEnd();
}

void QuadraticCurve::drawControlGeometry()
{
    list<Vertex>::iterator vIter;

    if (!this->showControls)
    {
        return;
    }
    
    if (this->points.size() != 0)
    {
        glDisable(GL_LIGHTING); // wont be needing this
        glColor3f(1.0, 0.0, 0.0); // everything will be red

        // draw the lines
        glLineWidth(1.0);
        vIter = this->points.begin();
        glBegin(GL_LINES);
            do
            {
                glVertex3dv(&(*vIter).prevOffCurvePoint[0]);
                glVertex3dv(&(*vIter).nextOffCurvePoint[0]);
                vIter++;
            }
            while(vIter != this->points.end());
        glEnd();

        // draw the points
        glPointSize(3.0);
        vIter = this->points.begin();
        glBegin(GL_POINTS);
            do
            {
                glVertex3dv(&(*vIter).prevOffCurvePoint[0]);
                glVertex3dv(&(*vIter).onCurvePoint[0]);
                glVertex3dv(&(*vIter).nextOffCurvePoint[0]);
                vIter++;
            }
            while(vIter != this->points.end());
        glEnd();
    }
}

void QuadraticCurve::drawEdgeTriangles()
{
    float   inside;
    GLint   univar1, attr1;
    vec3d   p1, p2, p3;
    vec3d   midpoint;

    list<Vertex>::iterator vIter;
    list<Vertex>::iterator nextIter;

    // turn on the shader
    this->shader->turnShadersOn();

    // customize the shader
    float color4[4] = {this->color[0], this->color[1], this->color[2], 1.0};
    univar1 = glGetUniformLocation(this->shader->getProgram(), "u_fgColor");
    glUniform4fv(univar1, 1, &color4[0]);

    // we will need this shader var
    attr1 = glGetAttribLocation(this->shader->getProgram(), "a_inside");

    // draw the edge triangles
    glEnable(GL_BLEND);
    glBegin(GL_TRIANGLES);
        vIter = this->points.begin();
        nextIter = vIter; nextIter++;
        do
        {
            if (nextIter == this->points.end())
            {
                nextIter = this->points.begin();
            }

            // calculate this once to save time
            midpoint = 0.5*(*vIter).nextOffCurvePoint + 
                       0.5*(*nextIter).prevOffCurvePoint;

            // indicate inside/outside to shader
            p1  = (*vIter).onCurvePoint;
            p2  = (*vIter).nextOffCurvePoint;
            p3  = midpoint;
            if ((*vIter).insideMask[2])
            {
                glVertexAttrib1f(attr1, 1.0f); 
            }
            else
            {
                glVertexAttrib1f(attr1, 0.0f);
            }
            glTexCoord2d(0.0, 0.0); glVertex3dv(&p1[0]); 
            glTexCoord2d(0.5, 0.0); glVertex3dv(&p2[0]); 
            glTexCoord2d(1.0, 1.0); glVertex3dv(&p3[0]);
             

            // indicate inside/outside to shader
            p1 = midpoint;
            p2 = (*nextIter).prevOffCurvePoint;
            p3 = (*nextIter).onCurvePoint;
            if ((*nextIter).insideMask[0])
            {
                glVertexAttrib1f(attr1, 1.0f); 
            }
            else
            {
                glVertexAttrib1f(attr1, 0.0f);
            }
            glTexCoord2d(0.0, 0.0); glVertex3dv(&p1[0]); 
            glTexCoord2d(0.5, 0.0); glVertex3dv(&p2[0]); 
            glTexCoord2d(1.0, 1.0); glVertex3dv(&p3[0]); 

            vIter++;
            nextIter++;
        }
        while(vIter != this->points.end());
    glEnd();

    // turn off the shader
    this->shader->turnShadersOff();
}

void QuadraticCurve::drawOpenCurve()
{
    vec<Point3D*>* curvePoints;
    vec<Point3D*>::iterator pIter;

    curvePoints = this->curve->getCurvePoints();

    if (curvePoints != NULL && curvePoints->size() > 0)
    {
        glColor3f(1.0, 0.0, 0.0);
        glBegin(GL_LINE_STRIP);
            pIter = curvePoints->begin();
            do
            {
                glVertex3d((*pIter)->x, (*pIter)->y, (*pIter)->z);
                pIter++;
            }
            while(pIter != curvePoints->end());
        glEnd();
    }
}